package com.zb.controller;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Map.Entry;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.jdom.JDOMException;

import com.zb.utils.MD5Util;
import com.zb.utils.WXUtil;
import com.zb.utils.XMLUtil;

/**
 * 下载对账单
 * 
 * 作者: zhoubang 
 * 日期：2015年10月30日 上午8:47:26
 */
public class DownloadBill {
    /**
     * 下载对账单所需参数
     */
    private final static String appid = "";//公众号APPID
    private final static String mch_id = "";//公众号对应的商户号
    private final static String bill_date = "";//查询日期
    private final static String bill_type = "ALL";//查询账单的类型,默认查询所有
    private static String key = "";//支付密钥，商户平台获取
    
    /**
     * 如果微信返回了 invalid bill_date ，，则，应该是对应的bill_date的日期当天，没有订单生成。
     * 
     * 作者: zhoubang 
     * 日期：2015年10月30日 上午8:58:34
     * @param args
     * @throws IOException 
     * @throws JDOMException 
     */
    @SuppressWarnings("unchecked")
    public static void main(String[] args) throws JDOMException, IOException {
        
        SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
        parameters.put("appid", appid); //appid
        parameters.put("mch_id", mch_id); //微信商户号
        parameters.put("nonce_str", WXUtil.getNonceStr()); //随机字符串
        parameters.put("bill_date", bill_date);//需要查询的日期，格式yyyymmdd，如：20150720，也就是2015-07-20
        
        /**
         * ALL，返回当日所有订单信息，默认值
         * SUCCESS，返回当日成功支付的订单
         * REFUND，返回当日退款订单
         * REVOKED，已撤销的订单
         */
        parameters.put("bill_type", bill_type);//返回当日所有订单信息，默认值.区分大小写
        
        
        //创建签名，算法与支付的算法一样
        String sign = createSign("UTF-8", parameters);
        parameters.put("sign", sign);
        
        //生成请求报文
        String requestXML = getRequestXml(parameters);
        
        HttpClient client = new HttpClient();
        PostMethod myPost = new PostMethod("https://api.mch.weixin.qq.com/pay/downloadbill");//查询对账单接口
        client.getParams().setSoTimeout(300 * 1000);
        String result = null;
        try {
            myPost.setRequestEntity(new StringRequestEntity(requestXML, "text/xml", "utf-8"));
            int statusCode = client.executeMethod(myPost);
            if (statusCode == HttpStatus.SC_OK) {
                BufferedInputStream bis = new BufferedInputStream(myPost.getResponseBodyAsStream());
                byte[] bytes = new byte[1024];
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                int count = 0;
                while ((count = bis.read(bytes)) != -1) {
                    bos.write(bytes, 0, count);
                }
                byte[] strByte = bos.toByteArray();
                result = new String(strByte, 0, strByte.length, "utf-8");
                bos.close();
                bis.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println();
        //输出微信返回的对账单信息
        System.out.println(result);
        
        /**
         * 当前日期没有生成对账单的话，微信则会返回下面这样的信息
         * 
         * <xml><return_code><![CDATA[FAIL]]></return_code>
         * <return_msg><![CDATA[invalid bill_date]]></return_msg>
         * </xml>
         * 
         */
        Map<String, String> map = XMLUtil.doXMLParse(result);//无论是否有对账单信息，先转换为map，再进行判断
        if(map.get("return_code").equals("FAIL")){//对账单没有查询到，当天日期没有生成对账单
            System.out.println("对账单没有查询到，当天日期没有生成对账单");
            
        }else{//有对账单信息
            
            //下面可以处理其他的业务
            
            //对对账单进行解析，由于数据是以文本表格的形式返回，也就是表头与数据之间有回车符(\n)，则，我们在解析的时候，可以进行分割，以 \n 进行分割，这样就获取了每一行的数据。
            //注意一点：由于账单数据的数量是未知的，所以在解析的时候，可以根据数据结果的倒数第一行的第一个字段数据，来判断有多少条对账单数据，这样也就可以进行遍历操作了
            
            //获取对账单数据结果的最后一行
            String[] dataRows = result.split("\n");//对结果进行分割
            String lastData = dataRows[dataRows.length - 1];//获取最后一行
            System.out.println(lastData);
            
            //获取对账单的数据条数，注意，每个参数前增加`符号，为标准键盘1左边键的字符，所以，要想获取数值，需要替换掉`符号，然后转换为整型
            int dataNums = Integer.parseInt(lastData.split(",")[0].replace("`", ""));
            System.out.println("对账单的数据条数是：" + dataNums);
            
            //获取每一条账单记录，由于第一行是账单表头，所以，下标应该从1开始，账单的数量就是dataNums
            for (int i = 1; i <= dataNums; i++) {
                System.out.println(dataRows[i]);//输出每一行的账单数据
                
                /*这里获取了每一行的账单信息之后，你就可以根据实际需要进行解析处理了...*/
                
            }
        }
    }

    /**
     * 生成下载对账单的请求报文
     * 
     * 作者: zhoubang 
     * 日期：2015年10月29日 下午1:57:11
     * @param parameters
     * @return
     */
    public static String getRequestXml(SortedMap<Object, Object> parameters) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set<Entry<Object, Object>> es = parameters.entrySet();
        Iterator<Entry<Object, Object>> it = es.iterator();
        while (it.hasNext()) {
            Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>) it.next();
            String k = (String) entry.getKey();
            String v = entry.getValue() + "";
            if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
                sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
            } else {
                sb.append("<" + k + ">" + v + "</" + k + ">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }
    
    /**
     * 生成签名
     * 
     * 作者: zhoubang 
     * 日期：2015年10月29日 下午1:57:05
     * @param characterEncoding
     * @param parameters
     * @return
     */
    public static String createSign(String characterEncoding, SortedMap<Object, Object> parameters) {
        StringBuffer sb = new StringBuffer();
        Set<Entry<Object, Object>> es = parameters.entrySet();
        Iterator<Entry<Object, Object>> it = es.iterator();
        while (it.hasNext()) {
            Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>) it.next();
            String k = (String) entry.getKey();
            Object v = entry.getValue();
            /** 如果参数为key或者sign，则不参与加密签名 */
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        /** 支付密钥必须参与加密，放在字符串最后面 */
        sb.append("key=" + key);
        /** 记得最后一定要转换为大写 */
        String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
        return sign;
    }

}

